package magicsquare;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;

class RowSequenceBuilder {
	final Queue<RowSequence> queue = new LinkedList<RowSequence>();
	final Set<RowSequence> sequenceSet = new HashSet<RowSequence>();
	RowSequence next;
	
	private RowSequenceBuilder() {
		final RowSequence firstRowSequence = buildFirstRowSequence();
		sequenceSet.add(firstRowSequence);
		queue.add(firstRowSequence);
		//findNexSequences(firstRowSequence);
		
		while (next() != null) {
		}
	}
	
	private void findNexSequences(final RowSequence rs) {
		rs.fillWithSetBits(Ctx.tmpBuf);
		for (int currIdx = 0, n = (Ctx.squareBase - 1); currIdx < n; currIdx++) {
			int step = 1;
			if ((Ctx.tmpBuf[currIdx + step]
					- Ctx.tmpBuf[currIdx] > 2)) {
				addToQueue(Ctx.tmpBuf, currIdx, step);
			}					
			for (step = 2; (step + currIdx) < Ctx.squareBase; step++) {
				if ((Ctx.tmpBuf[currIdx + 1] - Ctx.tmpBuf[currIdx] > 1) && (Ctx.tmpBuf[currIdx
								+ step]
								- Ctx.tmpBuf[currIdx + step - 1] > 1)) {
					addToQueue(Ctx.tmpBuf, currIdx, step);	
				}
			}
		}
	}

	private void addToQueue(final int[] bits, final int currIdx, final int step) {
		bits[currIdx]++;
		bits[currIdx + step]--;
		final RowSequence newRs = RowSequence.build(bits);
		final boolean addToQueue = sequenceSet.add(newRs);
		if (addToQueue) {
			queue.add(newRs);	
		} 
		bits[currIdx]--;
		bits[currIdx + step]++;
		//Ctx.printDebug("NEW="+addToQueue+" RowSequence: " + newRs + " - from: " + Arrays.toString(bits) + ", currIdx="+currIdx+", step="+step);
	}
	
//	@Override
//	public void remove() {
//		throw new UnsupportedOperationException();
//	}

//	@Override
//	public boolean hasNext() {
//		//Ctx.printDebug("hasNext(): " + queue.size());
//		return !queue.isEmpty();
//	}

	//@Override
	private RowSequence next() {
		final RowSequence result = queue.poll();
		if (result != null) {
			findNexSequences(result);
		}
		//Ctx.printDebug("Next: " + result);
		return result;
	}
	
	private void build() {
		final RowSequence firstRowSequence = buildFirstRowSequence();
		sequenceSet.add(firstRowSequence);
		queue.add(firstRowSequence);
		while ( ! queue.isEmpty() ) {
			final RowSequence result = queue.poll();
			if (result != null) {
				findNexSequences(result);
			}
		}

		//Ctx.printDebug("Next: " + result);
	}
	
	private static RowSequence buildFirstRowSequence() {
		RowSequence result = new RowSequence(Ctx.lasNumber);
		final int h = Ctx.squareBase / 2;
		if ((Ctx.squareBase % 2) == 1) {
			result.set(Ctx.lasBit / 2);
		}
		for (int i = 0; i < h; i++) {
			result.set(i);
			result.set(Ctx.lasBit - i);
		}
		return result;
	}
	
	public static RowSequences buildRowSequences() {
		final RowSequenceBuilder builder = new RowSequenceBuilder();
		builder.build();
		final RowSequence[] rowSequences = builder.sequenceSet.toArray(new RowSequence[builder.sequenceSet.size()]);
		return new RowSequences(rowSequences);
	}
	
}
